package indi.zhifa.recipe.bailan.framework.auth.util.impl;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.AsymmetricAlgorithm;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import indi.zhifa.recipe.bailan.framework.auth.entity.dto.encrypt.PasswdEncryptKey;
import indi.zhifa.recipe.bailan.framework.auth.entity.dto.encrypt.UserNameEncryptKey;
import indi.zhifa.recipe.bailan.framework.auth.entity.vo.PasswdEncryptKeyVo;
import indi.zhifa.recipe.bailan.framework.auth.util.IZfEncryptUtil;
import indi.zhifa.recipe.bailan.framework.common.entity.exception.ServiceException;
import indi.zhifa.recipe.bailan.framework.web.common.util.ZFHttpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpSession;
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;

@Slf4j
@Component
public class ZfEncryptUtilImpl implements IZfEncryptUtil {

    @Override
    public String aesKey() {
        return Base64.encode(genAesKeyByte());
    }


    protected byte[] genAesKeyByte(){
        HttpSession httpSession = ZFHttpUtil.getHttpSession();
        byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue(), AES_KEY_LEN).getEncoded();
        httpSession.setAttribute(AES_HTTP_SESSION_KEY,key);
        return key;
    }

    protected byte[] getAesKey(){
        HttpSession httpSession = ZFHttpUtil.getHttpSession();
        Object obj = httpSession.getAttribute(AES_HTTP_SESSION_KEY);
        if(null == obj){
            throw new ServiceException("还未生成aesKey");
        }
        return (byte[])obj;
    }

    @Override
    public String aesEncode(String pContent) {
        byte[] key = getAesKey();
        String encodeStr = SecureUtil.aes(key).encryptBase64(pContent.getBytes(StandardCharsets.UTF_8));
        return encodeStr;
    }

    @Override
    public String aesDecode(String pCiphertext) {
        byte[] key = getAesKey();
        String decryptStr = SecureUtil.aes(key).decryptStr(pCiphertext);
        return decryptStr;
    }

    protected byte[] genDesKeyByte(){
        HttpSession httpSession = ZFHttpUtil.getHttpSession();
        byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.DESede.getValue(), DES_KEY_LEN).getEncoded();
        httpSession.setAttribute(DES_HTTP_SESSION_KEY,key);
        return key;
    }

    protected byte[] getDesKey(){
        HttpSession httpSession = ZFHttpUtil.getHttpSession();
        Object obj = httpSession.getAttribute(DES_HTTP_SESSION_KEY);
        if(null == obj){
            throw new ServiceException("还未生成desKey");
        }
        return (byte[])obj;
    }

    @Override
    public String desKey() {
        return Base64.encode(genDesKeyByte());
    }

    @Override
    public String desEncode(String pContent) {
        byte[] key = getDesKey();
        String decodeStr = SecureUtil.desede(key).encryptBase64(pContent);
        return decodeStr;
    }

    @Override
    public String desDecode(String pCiphertext) {
        byte[] key = getDesKey();
        String decodeStr = SecureUtil.desede(key).decryptStr(pCiphertext);
        return decodeStr;
    }


    protected KeyPair getRsaKey(){
        HttpSession httpSession = ZFHttpUtil.getHttpSession();
        Object oKeyPair = httpSession.getAttribute(RSA_HTTP_SESSION_KEY);
        if(null == oKeyPair){
            throw new ServiceException("还未生成rsa-key");
        }
        return (KeyPair)oKeyPair;
    }

    @Override
    public String rsaKey() {
        KeyPair keyPair = SecureUtil.generateKeyPair(AsymmetricAlgorithm.RSA.getValue());
        HttpSession httpSession = ZFHttpUtil.getHttpSession();
        httpSession.setAttribute(RSA_HTTP_SESSION_KEY,keyPair);
        return Base64.encode(keyPair.getPublic().getEncoded());
    }


    @Override
    public String rsaEncode(String pContent) {
        KeyPair keyPair = getRsaKey();
        byte[] publicKey = keyPair.getPublic().getEncoded();
        byte[] privateKey = keyPair.getPrivate().getEncoded();
        RSA rsa = new RSA(privateKey,publicKey);
        String ciphertext = rsa.encryptBase64(pContent, KeyType.PublicKey);
        return ciphertext;
    }

    @Override
    public String rsaDecode(String pCiphertext) {
        KeyPair keyPair = getRsaKey();
        byte[] publicKey = keyPair.getPublic().getEncoded();
        byte[] privateKey = keyPair.getPrivate().getEncoded();
        String content = SecureUtil.rsa(privateKey,publicKey).decryptStr(pCiphertext,KeyType.PrivateKey);
        return content;
    }

    @Override
    public String sm2Key() {
        SM2 sm2 = new SM2();
        KeyPair keyPair = new KeyPair(sm2.getPublicKey(),sm2.getPrivateKey());
        HttpSession httpSession = ZFHttpUtil.getHttpSession();
        httpSession.setAttribute(SM2_HTTP_SESSION_KEY,keyPair);
        return Base64.encode(keyPair.getPublic().getEncoded());
    }

    protected  KeyPair getSm2Key(){
        HttpSession httpSession = ZFHttpUtil.getHttpSession();
        Object oKeyPair = httpSession.getAttribute(SM2_HTTP_SESSION_KEY);
        if(null == oKeyPair){
            throw new ServiceException("还未生成sm2-key");
        }
        return (KeyPair)oKeyPair;
    }

    @Override
    public String sm2Encode(String pContent) {
        KeyPair keyPair = getSm2Key();
        SM2 sm2 = new SM2(keyPair.getPrivate(),keyPair.getPublic());
        byte[] cipherCode = sm2.encrypt(pContent,KeyType.PublicKey);
        byte[] signCode = sm2.sign(pContent.getBytes(StandardCharsets.UTF_8));
        String ciphertext = Base64.encode(cipherCode)+"."+Base64.encode(signCode);
        return ciphertext;
    }

    @Override
    public String sm2Decode(String pCiphertext) {

        String[] cipherAndSign = pCiphertext.split("\\.");
        if(null == cipherAndSign|| cipherAndSign.length != 2){
            throw new ServiceException("密文格式不对");
        }
        KeyPair keyPair = getSm2Key();
        SM2 sm2 = new SM2(keyPair.getPrivate(),keyPair.getPublic());
        String content = sm2.decryptStr(cipherAndSign[0],KeyType.PrivateKey);
        if(!sm2.verify(content.getBytes(StandardCharsets.UTF_8),Base64.decode(cipherAndSign[1]))){
            throw new ServiceException("签名验证不通过");
        }
        return content;
    }

    @Override
    public String sm3Digest(String pContent) {
        return SmUtil.sm3(pContent);
    }

    @Override
    public String md5Digest(String pContent) {
        return SecureUtil.md5(pContent);
    }

    @Override
    public String userNameKey() {
        SM2 sm2 = new SM2();
        UserNameEncryptKey userNameEncryptKey = new UserNameEncryptKey();
        userNameEncryptKey.setPublicKey(sm2.getPublicKey().getEncoded());
        userNameEncryptKey.setPrivateKey(sm2.getPrivateKey().getEncoded());
        HttpSession httpSession = ZFHttpUtil.getHttpSession();
        httpSession.setAttribute(USER_ENCRYPT_HTTP_SESSION_KEY,userNameEncryptKey);
        return Base64.encode(userNameEncryptKey.getPublicKey());
    }

    protected UserNameEncryptKey getUserNameKey(){
        HttpSession httpSession = ZFHttpUtil.getHttpSession();
        Object oUserNameEncryptKey = httpSession.getAttribute(USER_ENCRYPT_HTTP_SESSION_KEY);
        if(null == oUserNameEncryptKey){
            throw new ServiceException("用户名编码没有生成加密密钥");
        }
        return (UserNameEncryptKey)oUserNameEncryptKey;
    }

    @Override
    public String userNameEncode(String pUserName) {
        UserNameEncryptKey userNameEncryptKey = getUserNameKey();
        SM2 sm2 = new SM2(userNameEncryptKey.getPrivateKey(),userNameEncryptKey.getPublicKey());
        String cipherText = sm2.encryptBase64(pUserName,KeyType.PublicKey);
        return cipherText;
    }

    @Override
    public String userNameDecode(String pCiphertext) {
        UserNameEncryptKey userNameEncryptKey = getUserNameKey();
        SM2 sm2 = new SM2(userNameEncryptKey.getPrivateKey(),userNameEncryptKey.getPublicKey());
        String contentTest = sm2.decryptStr(pCiphertext,KeyType.PrivateKey);
        return contentTest;
    }

    @Override
    public PasswdEncryptKeyVo passwdKey() {
        PasswdEncryptKey passwdEncryptKey = new PasswdEncryptKey();
        SM2 sm2 = new SM2();
        passwdEncryptKey.setPrivateKey(sm2.getPrivateKey().getEncoded());
        passwdEncryptKey.setPublicKey(sm2.getPublicKey().getEncoded());
        passwdEncryptKey.setSalt(RandomUtil.randomString(PASSWD_SALT_LEN));
        HttpSession httpSession = ZFHttpUtil.getHttpSession();
        httpSession.setAttribute(PASSWD_ENCRYPT_HTTP_SESSION_KEY,passwdEncryptKey);
        PasswdEncryptKeyVo passwdEncryptKeyVo = new PasswdEncryptKeyVo();
        passwdEncryptKeyVo.setPublicKey( Base64.encode(sm2.getPrivateKey().getEncoded()));
        passwdEncryptKeyVo.setSalt(passwdEncryptKey.getSalt());
        return passwdEncryptKeyVo;
    }

    protected PasswdEncryptKey getPasswdKey(){
        HttpSession httpSession = ZFHttpUtil.getHttpSession();
        Object oPasswdEncryptKey = httpSession.getAttribute(PASSWD_ENCRYPT_HTTP_SESSION_KEY);
        if(null == oPasswdEncryptKey){
            throw new ServiceException("没有生成密码的加密密钥");
        }
        return (PasswdEncryptKey)oPasswdEncryptKey;
    }

    @Override
    public String passwdEncode(String pPasswd) {
        PasswdEncryptKey passwdEncryptKey = getPasswdKey();
        SM2 sm2 = new SM2(passwdEncryptKey.getPrivateKey(), passwdEncryptKey.getPublicKey());
        String cipherText = sm2.encryptBase64(pPasswd,KeyType.PublicKey);
        String signStr = SmUtil.sm3(cipherText+passwdEncryptKey.getSalt());
        return signStr+cipherText;
    }

    @Override
    public String passwdDecode(String pCiphertext) {
        PasswdEncryptKey passwdEncryptKey = getPasswdKey();
        if(pCiphertext.length() <= 64){
            throw new ServiceException("密码密文格式不对，缺失签名");
        }
        String signStr = pCiphertext.substring(0,64);
        String cipherText = pCiphertext.substring(64);
        String testSignStr = SmUtil.sm3(cipherText+passwdEncryptKey.getSalt());
        if(!testSignStr.equals(signStr)){
            throw new ServiceException("签名验证不通过");
        }
        SM2 sm2 = new SM2(passwdEncryptKey.getPrivateKey(),passwdEncryptKey.getPublicKey());
        String passwd = sm2.decryptStr(cipherText,KeyType.PrivateKey);
        return passwd;
    }

}
